package com.net.xpay.core.utils;

import com.google.common.base.Strings;
import org.apache.http.*;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.*;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultRedirectStrategy;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.HttpContext;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.TrustStrategy;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

/**
 * 推送第三方专用的，别的业务不要用这个，超时时间为3秒
 */

public class NotifyHttpClientUtil {
    private static final NotifyHttpClientUtil INSTANCE = new NotifyHttpClientUtil();

    private CloseableHttpClient httpClient;

    //这两个参数可以放到配置文件，构建httpclient的时候一定要设置这两个参数。很多故障都由此引起
    private final int TIMEOUT_SECONDS = 3 * 1000;   //10s

    private final int CONNECTION_REQUEST_TIMEOUT = 60 * 1000;//60s

    private final int POOL_SIZE = 300;

    private static final int CONNECT_ROUTE = 100;//设置每个路由的基础连接数

    private static final int VALIDATE_TIME = 30000;//设置重用连接时间

    private static Logger LOGGER = LoggerFactory.getLogger(NotifyHttpClientUtil.class);

    // 创建包含connection pool与超时设置的client
    private NotifyHttpClientUtil() {
        //setCircularRedirectsAllowed有风险.访问Tmall有域名店铺的时候， 会有circle redirect, 但是参数不同. setCircularRedirectsAllowed必须设置为true
        RequestConfig requestConfig = RequestConfig.custom()
                .setSocketTimeout(TIMEOUT_SECONDS)
                .setConnectTimeout(TIMEOUT_SECONDS)
                .setConnectionRequestTimeout(CONNECTION_REQUEST_TIMEOUT).setCircularRedirectsAllowed(true).build();

        HttpClientBuilder builder = HttpClientBuilder.create()
                .setDefaultRequestConfig(requestConfig);
        builder.setRedirectStrategy(new DefaultRedirectStrategy() {
            @Override
            public boolean isRedirected(HttpRequest request, HttpResponse response, HttpContext context) {
                boolean isRedirect = false;
                try {
                    isRedirect = super.isRedirected(request, response, context);
                } catch (ProtocolException e) {
                    LOGGER.error("redirect error ", e);
                }
                if (!isRedirect) {
                    int responseCode = response.getStatusLine().getStatusCode();
                    if (responseCode == 301 || responseCode == 302) {
                        return true;
                    }
                }
                return isRedirect;
            }
        });

        // setup a Trust Strategy that allows all certificates.
        SSLContext sslContext = null;
        try {
            sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
                @Override
                public boolean isTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
                    return true;
                }
            }).build();
        } catch (Exception e) {
            LOGGER.error("SSLContext setting error ...", e);
        }
        builder.setSSLContext(sslContext);

        // don't check Hostname, either.
        //      -- use SSLConnectionSocketFactory.getDefaultHostnameVerifier(), if you don't want to weaken
        HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;
        SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", sslSocketFactory)
                .build();

        //allows multi-threaded use
        PoolingHttpClientConnectionManager connMgr = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
        connMgr.setMaxTotal(POOL_SIZE);
        connMgr.setDefaultMaxPerRoute(CONNECT_ROUTE);
        builder.setConnectionManager(connMgr);

        httpClient = builder.build();
    }

    private void destroyApacheHttpClient() {
        try {
            if (httpClient != null) {
                httpClient.close();
                httpClient = null;
            }
        } catch (IOException e) {
            LOGGER.error("httpclient close fail", e);
        }
    }

    public static HttpGet getGetRequest() {
        return new HttpGet();
    }

    public static HttpPost getPostRequest() {
        return new HttpPost();
    }

    public static HttpPut getPutRequest() {
        return new HttpPut();
    }

    public static HttpDelete getDeleteRequest() {
        return new HttpDelete();
    }

    public static NotifyHttpClientUtil getInstance() {
        return INSTANCE;
    }

    public String executeAndGet(HttpRequestBase httpRequestBase, HttpContext httpContext, String encode) throws Exception {
        CloseableHttpResponse response = null;

        String entityStr;
        Long start = System.currentTimeMillis();

        try {
            response = httpClient.execute(httpRequestBase, httpContext);

            if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                LOGGER.error("请求地址：" + httpRequestBase.getURI() + ", 请求方法：" + httpRequestBase.getMethod()
                        + ",STATUS CODE = " + response.getStatusLine().getStatusCode());

                throw new Exception("Response Status Code : " + response.getStatusLine().getStatusCode());
            } else {
                HttpEntity entity = response.getEntity();
                if (entity != null) {
                    String actualEncode = getEncode(response, encode);
                    entityStr = EntityUtils.toString(entity, actualEncode);
                } else {
                    throw new Exception("Response Entity Is Null");
                }
            }
        } finally {
            if (response != null) {
                response.close();
            }
            if (httpRequestBase != null) {
                httpRequestBase.releaseConnection();
            }
            Long end = System.currentTimeMillis();
            LOGGER.info("url:" + httpRequestBase.getURI().toString() + ",cost time:" + (end - start) + "ms");
        }
        return entityStr;
    }

    public byte[] executeAndGet(HttpRequestBase httpRequestBase, HttpContext httpContext) throws Exception {
        CloseableHttpResponse response = null;

        byte[] entityBytes;
        Long start = System.currentTimeMillis();

        try {
            response = httpClient.execute(httpRequestBase, httpContext);

            if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                LOGGER.error("请求地址：" + httpRequestBase.getURI() + ", 请求方法：" + httpRequestBase.getMethod()
                        + ",STATUS CODE = " + response.getStatusLine().getStatusCode());

                throw new Exception("Response Status Code : " + response.getStatusLine().getStatusCode());
            } else {
                HttpEntity entity = response.getEntity();
                if (entity != null) {
                    entityBytes = EntityUtils.toByteArray(entity);
                } else {
                    throw new Exception("Response Entity Is Null");
                }
            }
        } finally {
            if (response != null) {
                response.close();
            }
            if (httpRequestBase != null) {
                httpRequestBase.releaseConnection();
            }
            Long end = System.currentTimeMillis();
            LOGGER.info("url:" + httpRequestBase.getURI().toString() + ",cost time:" + (end - start) / 1000 + "s");
        }
        return entityBytes;
    }

    private String getEncode(CloseableHttpResponse response, String encode) {
        if (Strings.isNullOrEmpty(encode)) {
            encode = "UTF-8";
        }
        Header[] hs = response.getHeaders("Content-Type");
        if (hs == null || hs.length == 0) {
            return encode;
        }
        for (Header header : hs) {
            String value = header.getValue();
            if (value != null && value.toUpperCase().contains("GBK")) {
                encode = "GBK";
                break;
            } else if (value != null && value.toUpperCase().contains("UTF-8")) {
                encode = "UTF-8";
                break;
            }
        }
        return encode;
    }
    //
    //    public static void main(String[] args) throws Exception {
    //        String req = "http://shop117316962.taobao.com";
    //        HttpGet httpGet = HttpClientUtil.getGetRequest();
    //        httpGet.setURI(new URI(req));
    //        String res = HttpClientUtil.getInstance().executeAndGet(httpGet, null, "GBK");
    //        int a = res.indexOf("rate.taobao.com/user-rate-");
    //        if (a == -1) {
    //            LOGGER.error("抓取用户uid,url=" + "http://shop117316962.taobao.com/");
    //        } else {
    //            int b = res.indexOf(".htm", a);
    //            System.out.println("uid=" + res.substring(a + 26, b));
    //        }
    //    }
}